package org.softee.extractor.sax.helper;

public class XmlEscaper {
    private static final char AMP = '&';
    private static final String AMP_ENTITY = "&amp;";
    private static final char LT = '<';
    private static final String LT_ENTITY = "&lt;";
    private static final char GT = '>';
    private static final String GT_ENTITY = "&gt;";
    private static final char QUOT = '"';
    private static final String QUOT_ENTITY = "&quot;";
    private static final char APOS = '\'';
    private static final String APOS_ENTITY = "&apos";

    public static String escapeText(String text) {
        return escape(text, false);
    }

    public static String quotedAttributeText(String text) {
        // one could look at whether the text contains QUOT and wrap value in APOS, but it is too
        // non-deterministic in my view, and Xerces serialization doesn't do it either
        return QUOT + escape(text, true) + QUOT;
    }
    
    /**
     * 
     * @param s the string to XML escape
     * @param escapeQuot whether to escape " and &quot;
     * @return an escaped version of s. A reference to s itself is returned, if no escaping occurred
     */
    private static String escape(String s, boolean escapeQuot) {
        char[] chars = s.toCharArray();
        // The string builder is not initialized until an escapable character is detected.
        // As most strings are unlikely to contain escapable 
        StringBuilder sb = null;
        for (int i = 0; i < chars.length; i++) {
            char ch = chars[i];
            String repl = null;
            switch (ch) {
            case AMP:
                repl = AMP_ENTITY;
                break;
            case LT:
                repl = LT_ENTITY;
                break;
            case GT:
                repl = GT_ENTITY;
            case QUOT:
                if (escapeQuot) {
                    repl = QUOT_ENTITY;
                }
            default:
                break;
            }
            if (repl != null) {
                if (sb == null) {
                    // initialize buffer to hold "a few" more characters (125% + 16) to allow escaping without buffer expansion
                    int reserve = chars.length + (chars.length >>> 2) + 16;
                    sb = new StringBuilder(reserve);
                    // Initialize with the "skipped" characters 
                    sb.append(chars, 0, i);
                }
                sb.append(repl);
            } else {
                if (sb != null) {
                    sb.append(ch);
                }
            }
        }
        return (sb != null) ? sb.toString() : s;
    }
}
